package pers.chao.tool.infrastructure.exception;

import pers.chao.tool.infrastructure.ParameterFormatUtil;

import java.util.Arrays;

/**
 * Description: 支持格式化字符串的自定义运行时异常
 *
 * @author W.Chao
 * @date 2020/11/7 15:39
 **/
public class FormativeException extends RuntimeException {
	private static final long serialVersionUID = -1;
	private int[] indices;
	private int usedCount;
	private String message;
	private transient Throwable throwable;

	public FormativeException() {
		super();
	}

	public FormativeException(String message) {
		this.message = message;
	}

	public FormativeException(Throwable cause) {
		this.throwable = cause;
		this.message = Arrays.toString(cause.getStackTrace());
	}

	public FormativeException(String format, Object... args) {
		init(format, args);
		fillInStackTrace();
		this.message = formatMessage(format, args);
		if (throwable != null) {
			this.message += System.lineSeparator() + Arrays.toString(throwable.getStackTrace());
		}
	}

	private void init(String format, Object... args) {
		final int len = Math.max(1, format == null ? 0 : format.length() >> 1); // 折半
		this.indices = new int[len];
		final int placeholders = ParameterFormatUtil.countArgumentPlaceholders2(format, indices);
		initThrowable(args, placeholders);
		this.usedCount = Math.min(placeholders, args == null ? 0 : args.length);
	}

	/**
	 * 判断传入的参数最后一个是否是异常信息
	 *
	 * @param args
	 * @param usedArgs
	 */
	private void initThrowable(final Object[] args, final int usedArgs) {
		if (args != null) {
			final int argCount = args.length;
			if (usedArgs < argCount && this.throwable == null && args[argCount -1] instanceof Throwable) {
				this.throwable = (Throwable) args[argCount - 1];
			}
		}
	}

	private String formatMessage(String format, Object... args) {
		StringBuilder stringBuilder = new StringBuilder();
		if (indices[0] < 0) {
			ParameterFormatUtil.formatMessage(stringBuilder, format, args, usedCount);
		} else {
			ParameterFormatUtil.formatMessage2(stringBuilder, format, args, usedCount, indices);
		}
		return stringBuilder.toString();
	}

	@Override
	public String getMessage() {
		return message;
	}
}
